import typing as tp

STEP = int  # ms，第 (STEP / 1000) 秒

class Attr:
    pass

_Attr = tp.TypeVar("_Attr", bound=Attr)

class Player(tp.Generic[_Attr]):
    attr: _Attr

    @property
    def cd_min(self) -> float:
        """cd减少量"""
        return 0.0

class Skill(tp.Generic[_Attr]):
    origin_cd: float = 0
    before_play_cd: float = 0
    after_play_cd: float = 0
    max_times: int = 0
    current_times: int = 0
    is_by_role_effect: bool = False
    active_history_list: tp.List[STEP] = []
    beidong: tp.List[tp.Self] = []

    @property
    def shifa_steps(self) -> int:
        return int(self.before_play_cd * 1000)

    @property
    def jangzhi_steps(self) -> int:
        return int(self.after_play_cd * 1000)

    def get_real_cd(self, cd_min: float = 0) -> STEP:
        if self.is_by_role_effect:
            return int(round(self.origin_cd / (1 + cd_min), 1)) * 1000
        else:
            return int(self.origin_cd * 1000)

    def check_cd_is_ready_and_step_next(self, at: STEP, cd_min: float) -> bool:
        cd = self.get_real_cd(cd_min)
        if cd == 0:
            return True
        else:
            if self.current_times == 0:
                new_times = min(
                    self.current_times + int((at - self.active_history_list[-1]) // cd),
                    self.max_times
                )
                self.current_times = new_times
                return self.current_times > 0
            else:
                return True

    @staticmethod
    def condition_is_ready(player: Player[_Attr]) -> bool:
        return True

    def check_ready(self, at: STEP, player: Player[_Attr]) -> bool:
        return self.condition_is_ready(player) and self.check_cd_is_ready_and_step_next(at, player.cd_min)

class Dot(Skill[_Attr], tp.Generic[_Attr]):
    start_step: STEP = 0
    expired_step: STEP = 0
    level: int = 0

    def check_ready(self, at: STEP, player: Player[_Attr]) -> bool:
        return at >= self.start_step and not self.is_expired(at) and super().check_ready(at, player)

    def is_expired(self, at: STEP) -> bool:
        return at > self.start_step + self.expired_step

class Buff(Dot[_Attr], tp.Generic[_Attr]):
    def check_cd_is_ready_and_step_next(self, at: STEP, cd_min: float) -> bool:
        return True

class SkillLoopManager(tp.Generic[_Attr]):
    player: Player[_Attr]
    buffs: tp.List[Buff[_Attr]] = []
    dots: tp.List[Dot[_Attr]] = []
